package com.njtoyo.taxi.admin.service.finance.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Console;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.excel.StyleSet;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.njtoyo.taxi.admin.library.common.Enum;
import com.njtoyo.taxi.admin.library.payment.cmb.CmbPayClient;
import com.njtoyo.taxi.admin.logic.finance.FinanceLogic;
import com.njtoyo.taxi.admin.mapper.master.entity.*;
import com.njtoyo.taxi.admin.mapper.master.enums.*;
import com.njtoyo.taxi.admin.mapper.master.mapper.*;
import com.njtoyo.taxi.admin.mapper.master.enums.RideOrderState;
import com.njtoyo.taxi.admin.mapper.master.mapper.*;
import com.njtoyo.taxi.admin.mapper.secondary.entity.OrderBankBill;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardIncomeDaily;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardIncomeMinute;
import com.njtoyo.taxi.admin.mapper.secondary.entity.data.DashboardOrderDaily;
import com.njtoyo.taxi.admin.mapper.secondary.enums.DataIncomeThroughType;
import com.njtoyo.taxi.admin.mapper.secondary.enums.DataIncomeType;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.DingdingSendMsgMapper;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.OrderBankBillMapper;
import com.njtoyo.taxi.admin.mapper.secondary.mapper.data.DashboardIncomeDailyMapper;
import com.njtoyo.taxi.admin.rest.presenter.business.ride.scan_ride.ScanRideListPresenter;
import com.njtoyo.taxi.admin.rest.presenter.finance.*;
import com.njtoyo.taxi.admin.rest.presenter.platfrom.AdminUserIdentityPresenter;
import com.njtoyo.taxi.admin.rest.wrapper.finance.FinanceIncomeBelongStatWrapper;
import com.njtoyo.taxi.admin.rest.wrapper.finance.FinanceReconciliationWrapper;
import com.njtoyo.taxi.admin.rest.wrapper.finance.FinanceStatWrapper;
import com.njtoyo.taxi.admin.rest.wrapper.finance.dto.FinanceIncomeBelongStatDto;
import com.njtoyo.taxi.admin.rest.wrapper.finance.dto.FinanceReconciliationDto;
import com.njtoyo.taxi.admin.rest.wrapper.finance.dto.FinanceStatDto;
import com.njtoyo.taxi.admin.service.business.DriverWithdrawService;
import com.njtoyo.taxi.admin.service.finance.FinanceService;
import com.njtoyo.taxi.entity.backend.AdminUser;
import com.taxi.entity.common.RestResult;
import lombok.SneakyThrows;
import org.apache.poi.ss.usermodel.CellStyle;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.hutool.core.util.ObjectUtil.isNotEmpty;

@Service
public class FinanceServiceImpl implements FinanceService {
    @Autowired
    private FinanceLogic financeLogic;

    @Autowired
    private CompanyMapper companyMapper;
    @Autowired
    private IncomeBelongingLogMapper incomeBelongingLogMapper;
    @Autowired
    private OrderBankBillMapper orderBankBillMapper;
    @Autowired
    private DashboardIncomeDailyMapper dashboardIncomeDailyMapper;
    @Autowired
    private DriverWithdrawMapper driverWithdrawMapper;
    @Autowired
    private OrderBankBillMapper bankBillMapper;
    @Autowired
    private GeoCantonMapper geoCantonMapper;
    @Autowired
    private RideOrderMapper rideOrderMapper;
    @Autowired
    private CompanyWithdrawMapper companyWithdrawMapper;
    @Autowired
    private DingdingSendMsgMapper dingdingSendMsgMapper;

    @Override
    public RestResult<List<FinanceAllowanceStatsPresenter>> statistics(FinanceStatWrapper wrapper) {
        FinanceStatDto dto = new FinanceStatDto();
        dto.setIsAll(wrapper.getIsAll());
        if (!wrapper.getIsAll()) {
            if (ObjectUtil.isEmpty(wrapper.getStartDate()) || ObjectUtil.isEmpty(wrapper.getEndDate())) {
                return RestResult.failed("时间范围 不能为空");
            }

            dto.setStartDate(DateUtil.beginOfDay(wrapper.getStartDate()));
            dto.setEndDate(DateUtil.endOfDay(wrapper.getEndDate()));
            dto.setStartTimeStamp((int) (dto.getStartDate().getTime() / 1000));
            dto.setEndTimeStamp((int) (dto.getEndDate().getTime() / 1000));
        }

        // 循环区域
        List<GeoCanton> cantonList = financeLogic.selectCantonExcludeBlackIds();
        dto.setCantonList(cantonList);

        List<FinanceAllowanceStatsPresenter> res = new ArrayList<>();

        List<FinanceAllowanceStatsPresenter> allowanceStat = financeLogic.allowanceStat(dto);
        List<FinanceAllowanceStatsPresenter> couponStat = financeLogic.couponStat(dto);

        FinanceAllowanceStatsPresenter platformRes = new FinanceAllowanceStatsPresenter();
        FinanceAllowanceStatsPresenter.MainBody platformMainBodyRes = new FinanceAllowanceStatsPresenter.MainBody();
        platformMainBodyRes.setMainBodyType(Enum.MainBodyType.platform);
        platformRes.setMainBody(platformMainBodyRes);


        Map<Long, FinanceAllowanceStatsPresenter> allowanceStatMap = new HashMap<>();
        Map<Long, FinanceAllowanceStatsPresenter> couponStatMap = new HashMap<>();

        if (ObjectUtil.isNotEmpty(allowanceStat)) {
            for (FinanceAllowanceStatsPresenter i : allowanceStat) {

                if (Enum.MainBodyType.platform.equals(i.getMainBody().getMainBodyType())) {
                    platformRes.setMainData(i.getMainData());
                } else {
                    allowanceStatMap.put(i.getMainBody().getId(), i);
                }
            }
        }

        if (ObjectUtil.isNotEmpty(couponStat)) {
            for (FinanceAllowanceStatsPresenter i : couponStat) {
                if (Enum.MainBodyType.platform.equals(i.getMainBody().getMainBodyType())) {
                    platformRes.setMainData(mergeMainData(platformRes.getMainData(), i.getMainData()));
                } else {
                    couponStatMap.put(i.getMainBody().getId(), i);
                }
            }
        }

        // 设置平台
        res.add(platformRes);

        cantonList.forEach(canton -> {
            FinanceAllowanceStatsPresenter cantonItemRes = new FinanceAllowanceStatsPresenter();
            FinanceAllowanceStatsPresenter.MainBody cantonResBody = new FinanceAllowanceStatsPresenter.MainBody();

            FinanceAllowanceStatsPresenter allowance = allowanceStatMap.get(canton.getId());
            FinanceAllowanceStatsPresenter coupon = couponStatMap.get(canton.getId());

            if (ObjectUtil.isNotEmpty(allowance)) {
                cantonResBody = allowance.getMainBody();
            } else {
                cantonResBody = coupon.getMainBody();
            }

            cantonItemRes.setMainBody(cantonResBody);
            cantonItemRes.setMainData(mergeMainData(allowance.getMainData(), coupon.getMainData()));

            res.add(cantonItemRes);
        });

        return RestResult.success(res);
    }

    private FinanceAllowanceStatsPresenter.MainData mergeMainData(FinanceAllowanceStatsPresenter.MainData allowance, FinanceAllowanceStatsPresenter.MainData coupon) {
        FinanceAllowanceStatsPresenter.MainData res = BeanUtil.copyProperties(allowance, FinanceAllowanceStatsPresenter.MainData.class);

        res.setCouponGrantedCount(coupon.getCouponGrantedCount());
        res.setCouponUsedCount(coupon.getCouponUsedCount());
        res.setCouponUsedRate(coupon.getCouponUsedRate());

        return res;
    }

    @Override
    public RestResult<List<FinanceIncomeBelongStatsPresenter>> incomeBelongStatistics(AdminUser adminUser, FinanceIncomeBelongStatWrapper wrapper) {
        if (StrUtil.isBlank(wrapper.getSelectDate())) {
            wrapper.setSelectDate(DateUtil.formatDate(DateUtil.date()));
        }
        List<FinanceIncomeBelongStatsPresenter> result = new ArrayList<>();
        com.njtoyo.taxi.entity.backend.Enum.AccountType accountType = adminUser.getAccountType();
        if (Objects.equals(com.njtoyo.taxi.entity.backend.Enum.AccountType.company, accountType)) {
            AdminUserIdentityPresenter adminUserIdentityPresenter = (AdminUserIdentityPresenter) adminUser.getIdentity();
            if (adminUserIdentityPresenter != null) {
                List<Long> companyIds = adminUserIdentityPresenter.getCompanyIds();
                LambdaQueryWrapper<Company> lambdaQueryWrapper = new LambdaQueryWrapper<>();
                lambdaQueryWrapper.in(Company::getId, companyIds);
                List<Company> companyList = companyMapper.selectList(lambdaQueryWrapper);
                doIncomeBelongStatistics(result, wrapper, companyList);
            }
        } else if (Objects.equals(com.njtoyo.taxi.entity.backend.Enum.AccountType.canton, accountType)) {
            AdminUserIdentityPresenter identity = (AdminUserIdentityPresenter) adminUser.getIdentity();
//            List<Long> companyIds = identity.getCompanyIds();
//            LambdaQueryWrapper<Company> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//            lambdaQueryWrapper.in(Company::getId, companyIds);
//            List<Company> companyList = companyMapper.selectList(lambdaQueryWrapper);
//            doIncomeBelongStatistics(result, wrapper, companyList);
            for (Long cantonId : identity.getCantonIds()) {
                doIncomeBelongCantonStatistics(result, wrapper, cantonId);
            }
        } else {
            doIncomeBelongStatistics(result, wrapper);
        }


        Collections.sort(result, Comparator.comparing(m -> m.getOrderAmount(), Comparator.reverseOrder()));
        return RestResult.success(result);
    }

    private void doIncomeBelongStatistics(List<FinanceIncomeBelongStatsPresenter> result, FinanceIncomeBelongStatWrapper wrapper, List<Company> companyList) {
        if (CollUtil.isNotEmpty(companyList)) {
            List<Long> companyIds = companyList.stream().map(Company::getId).collect(Collectors.toList());
            List<Map<String, Object>> orderMaps = incomeBelongingLogMapper.selectMaps(new QueryWrapper<IncomeBelongingLog>()
                    .select("IFNULL(SUM(company_earning),0) as amount,company_id AS companyId")
                    .lambda()
                    .ge(IncomeBelongingLog::getTaskTime, wrapper.getStartDate())
                    .in(IncomeBelongingLog::getCompanyId, companyIds).groupBy(IncomeBelongingLog::getCompanyId));
            Map<Object, Object> orderToMap = orderMaps.stream().collect(Collectors.toMap(map -> map.get("companyId"), map -> map.get("amount")));
            System.out.println(orderMaps);
            //信息服务费
            LambdaQueryWrapper<DashboardIncomeDaily> queryWrapper = new QueryWrapper<DashboardIncomeDaily>()
                    .select("IFNULL(SUM(value),0) as amount,company_id AS companyId")
                    .lambda();
            if (StrUtil.isNotBlank(wrapper.getSelectStartDate()))
                queryWrapper.eq(DashboardIncomeDaily::getStatsDate, wrapper.getSelectStartDate());
            if (StrUtil.isNotBlank(wrapper.getSelectEndDate()))
                queryWrapper.le(DashboardIncomeDaily::getStatsDate, wrapper.getSelectEndDate());

            queryWrapper.eq(DashboardIncomeDaily::getType, DataIncomeType.infoservice)
                    .in(DashboardIncomeDaily::getCompanyId, companyIds)
                    .groupBy(DashboardIncomeDaily::getCompanyId);


            for (Company company : companyList) {
                FinanceIncomeBelongStatsPresenter presenter = new FinanceIncomeBelongStatsPresenter();
                presenter.setType(2);
                presenter.setId(company.getId().toString());
                presenter.setCompanyId(company.getId());
                presenter.setName(company.getName());
                if (MapUtil.isNotEmpty(orderToMap)) {
                    BigDecimal amount = ObjectUtil.isNull(orderToMap.get(company.getId())) ? BigDecimal.ZERO : Convert.toBigDecimal(orderToMap.get(company.getId()));
                    presenter.setOrderAmount(amount);
                    presenter.setDriverAmount(amount);
                }
                BigDecimal infoAmount = companyMapper.sumInfoServiceById(company.getId());
                presenter.setInfoAmount(infoAmount);
                result.add(presenter);
            }
        }
    }

    private void doIncomeBelongCantonStatistics(List<FinanceIncomeBelongStatsPresenter> result, FinanceIncomeBelongStatWrapper wrapper, Long cantonId) {
        if (ObjectUtil.isNull(wrapper.getCantonId())) {
            GeoCanton geoCanton = geoCantonMapper.selectById(cantonId);
            List<Company> companyAll = companyMapper.selectList(Wrappers.<Company>lambdaQuery().eq(Company::getCantonId, geoCanton.getId()));
            Map<Long, List<Company>> companyToMap = companyAll.stream().collect(Collectors.groupingBy(Company::getCantonId));
            FinanceIncomeBelongStatsPresenter presenter = new FinanceIncomeBelongStatsPresenter();
            presenter.setType(1);
            presenter.setId(geoCanton.getId().toString());
            presenter.setCantonId(geoCanton.getId());
            presenter.setName(geoCanton.getName());
            List<Company> companies = companyToMap.get(geoCanton.getId());
            if (CollUtil.isNotEmpty(companies)) {
                List<Long> companyIds = companies.stream().map(Company::getId).collect(Collectors.toList());
                wrapper.setCompanyIds(companyIds);
                BigDecimal amount = companyMapper.selectCompanyAmount(wrapper);
                Console.log("地区：{},交易额：{}", geoCanton.getName(), amount);

                presenter.setOrderAmount(Convert.toBigDecimal(amount));
            }
            result.add(presenter);
        } else {
            List<Company> companyList = companyMapper.selectList(Wrappers.<Company>lambdaQuery().in(Company::getCantonId, wrapper.getCantonId()));
            for (Company company : companyList) {
                wrapper.setCompanyId(company.getId());
                Map<String, Object> amountMap = companyMapper.selectCompanyAmountById(wrapper);
                FinanceIncomeBelongStatsPresenter presenter = new FinanceIncomeBelongStatsPresenter();
                presenter.setType(1);
                presenter.setId(wrapper.getCantonId().toString());
                presenter.setCantonId(wrapper.getCantonId());
                presenter.setName(company.getName());
                presenter.setCompanyId(company.getId());
                presenter.setCompanyName(company.getName());
                presenter.setOrderAmount(MapUtil.get(amountMap, "orderAmount", BigDecimal.class));
                presenter.setDriverAmount(MapUtil.get(amountMap, "driverEarnAmount", BigDecimal.class));
                presenter.setInfoAmount(MapUtil.get(amountMap, "infoServiceAmount", BigDecimal.class));
                result.add(presenter);

            }
        }
    }

    private void doIncomeBelongStatistics(List<FinanceIncomeBelongStatsPresenter> result, FinanceIncomeBelongStatWrapper wrapper) {
        if (ObjectUtil.isNull(wrapper.getCantonId())) {
            List<GeoCanton> geoCantons = geoCantonMapper.selectList(null);
            List<Long> cantonIds = geoCantons.stream().map(GeoCanton::getId).collect(Collectors.toList());
            List<Company> companyAll = companyMapper.selectList(Wrappers.<Company>lambdaQuery().in(Company::getCantonId, cantonIds));
            Map<Long, List<Company>> companyToMap = companyAll.stream().collect(Collectors.groupingBy(Company::getCantonId));
            for (GeoCanton geoCanton : geoCantons) {
                FinanceIncomeBelongStatsPresenter presenter = new FinanceIncomeBelongStatsPresenter();
                presenter.setType(1);
                presenter.setId(geoCanton.getId().toString());
                presenter.setCantonId(geoCanton.getId());
                presenter.setName(geoCanton.getName());
                List<Company> companies = companyToMap.get(geoCanton.getId());
                if (CollUtil.isNotEmpty(companies)) {
                    List<Long> companyIds = companies.stream().map(Company::getId).collect(Collectors.toList());
                    LambdaQueryWrapper<IncomeBelongingLog> queryWrapper = new QueryWrapper<IncomeBelongingLog>()
                            .select("IFNULL(SUM(company_earning),0)")
                            .lambda();
                    if (StrUtil.isNotBlank(wrapper.getSelectStartDate()))
                        queryWrapper.ge(IncomeBelongingLog::getTaskTime, wrapper.getSelectStartDate());
                    if (StrUtil.isNotBlank(wrapper.getSelectEndDate()))
                        queryWrapper.le(IncomeBelongingLog::getTaskTime, wrapper.getSelectEndDate());
                    queryWrapper.in(IncomeBelongingLog::getCompanyId, companyIds);
                    String amount = incomeBelongingLogMapper.selectObjs(queryWrapper).get(0).toString();
                    Console.log("地区：{},交易额：{}", geoCanton.getName(), amount);

                    presenter.setOrderAmount(Convert.toBigDecimal(amount));
                }
                result.add(presenter);
            }
        } else {
            List<Company> companyList = companyMapper.selectList(Wrappers.<Company>lambdaQuery().in(Company::getCantonId, wrapper.getCantonId()));
            doIncomeBelongStatistics(result, wrapper, companyList);
        }
    }

    @Override
    public RestResult<FinanceCustomerAllowancePresenter> customerAllowance(FinanceReconciliationWrapper wrapper) {
        FinanceReconciliationDto dto = new FinanceReconciliationDto();
        dto.setStartTime(DateUtil.beginOfDay(wrapper.getStartDate()));
        dto.setEndTime(DateUtil.endOfDay(wrapper.getEndDate()));

        FinanceCustomerAllowancePresenter res = financeLogic.customerAllowance(dto);

        return RestResult.success(res);
    }

    @Override
    public RestResult<FinanceServiceFeePresenter> serviceFee(FinanceReconciliationWrapper wrapper) {
        FinanceReconciliationDto dto = new FinanceReconciliationDto();
        dto.setStartTime(DateUtil.beginOfDay(wrapper.getStartDate()));
        dto.setEndTime(DateUtil.endOfDay(wrapper.getEndDate()));

        List<DashboardIncomeDaily> list = dashboardIncomeDailyMapper.selectList(Wrappers.<DashboardIncomeDaily>lambdaQuery()
                .eq(DashboardIncomeDaily::getType, 5)
                .ge(DashboardIncomeDaily::getStatsDate, dto.getStartTime())
                .le(DashboardIncomeDaily::getStatsDate, dto.getEndTime())
                .eq(DashboardIncomeDaily::getThroughType, DataIncomeThroughType.online));
        BigDecimal value = list.stream().map(DashboardIncomeDaily::getValue).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);

        FinanceServiceFeePresenter res = new FinanceServiceFeePresenter();
        res.setValue(value);

        return RestResult.success(res);
    }

    @Override
    public RestResult<FinanceDriverRewardPresenter> driverReward(FinanceReconciliationWrapper wrapper) {
        FinanceReconciliationDto dto = new FinanceReconciliationDto();
        dto.setStartTime(DateUtil.beginOfDay(wrapper.getStartDate()));
        dto.setEndTime(DateUtil.endOfDay(wrapper.getEndDate()));

        FinanceDriverRewardPresenter res = financeLogic.driverReward(dto);

        return RestResult.success(res);
    }

    @Override
    public RestResult<FinanceDriverFundsInOutPresenter> driverFundsInOut(FinanceReconciliationWrapper wrapper) {
        FinanceReconciliationDto dto = new FinanceReconciliationDto();
        dto.setStartTime(DateUtil.beginOfDay(wrapper.getStartDate()));
        dto.setEndTime(DateUtil.endOfDay(wrapper.getEndDate()));

        FinanceDriverFundsInOutPresenter res = financeLogic.driverFundsInOut(dto);

        return RestResult.success(res);
    }

    @Override
    public RestResult<FinanceUndrawnBalancePresenter> driverUndrawnBalance(FinanceReconciliationWrapper wrapper) {

        BigDecimal value = financeLogic.driverUndrawnBalance(DateUtil.endOfDay(wrapper.getEndDate()));

        FinanceUndrawnBalancePresenter res = new FinanceUndrawnBalancePresenter();
        res.setValue(value);

        return RestResult.success(res);
    }

    @Override
    public RestResult<FinancePaymentPlatformFeePresenter> paymentPlatformFee(FinanceReconciliationWrapper wrapper) {
        FinanceReconciliationDto dto = new FinanceReconciliationDto();
        dto.setStartTime(DateUtil.beginOfDay(wrapper.getStartDate()));
        dto.setEndTime(DateUtil.endOfDay(wrapper.getEndDate()));

        FinancePaymentPlatformFeePresenter res = financeLogic.paymentPlatformFee(dto);

        return RestResult.success(res);
    }

    @Override
    public RestResult<IPage<OrderBankBill>> getBankBillPage(Integer current, Integer size, String date, String result) {
        if (StrUtil.isBlank(date)) {
            date = DateUtil.formatDate(DateUtil.date());
        }
        Date startDate = DateUtil.beginOfDay(DateUtil.parseDate(date));
        Date endDate = DateUtil.endOfDay(DateUtil.parseDate(date));
        Page page = new Page<>(current, size);
        LambdaQueryWrapper<OrderBankBill> wrapper = Wrappers.<OrderBankBill>lambdaQuery()
                .between(OrderBankBill::getDealCompleteDate, startDate, endDate);
        if (StrUtil.isNotBlank(result)) {
            wrapper.eq(OrderBankBill::getResult, result);
        }
        IPage<OrderBankBill> pages = orderBankBillMapper.selectPage(page, wrapper);
        return RestResult.success(pages);
    }

    @Override
    public RestResult comparison(String startDate, String endDate) {
        Date startDateTime = DateUtil.beginOfDay(DateUtil.parseDate(startDate));
        Date endDateTime = DateUtil.endOfDay(DateUtil.parseDate(endDate));

        QueryWrapper<DashboardIncomeDaily> wrapper = new QueryWrapper<>();
        wrapper.between("stats_date", startDateTime, endDateTime)
                .eq("through_type", DataIncomeThroughType.all);
        wrapper.groupBy("type");
        wrapper.select("type, IFNULL(SUM(value), 0) AS total");
        List<Map<String, Object>> result = dashboardIncomeDailyMapper.selectMaps(wrapper);
        Map<String, Object> resultMap = new HashMap<>();
        Map result1 = result.stream().collect(Collectors.toMap((p) -> p.get("type"), (p) -> p.get("total")));
        Arrays.stream(DataIncomeType.values()).forEach(i -> {
            resultMap.put(i.name(), result1.getOrDefault(i.getCode(), 0));
        });

        QueryWrapper<RideOrder> queryWrapperAmount = new QueryWrapper<>();
        queryWrapperAmount.select("SUM(amount) AS totalAmount")
                .eq("state", RideOrderState.done)
                .eq("refunded", 0)
                .between("created_at", startDateTime, endDateTime);  // 使用 SUM 聚合函数
        Map<String, Object> resultAmount = rideOrderMapper.selectMaps(queryWrapperAmount).get(0);

        // 从结果中取出 totalAmount 并转换为 BigDecimal
        BigDecimal totalAmount = MapUtil.get(resultAmount, "totalAmount", BigDecimal.class);
        resultMap.put("totalRideAmount", totalAmount != null ? totalAmount : BigDecimal.ZERO);
        BigDecimal infoServiceAmount = dingdingSendMsgMapper.comIncomeByDate(startDateTime, endDateTime);
        resultMap.put("infoservice", infoServiceAmount);


        //提现
        QueryWrapper<DriverWithdraw> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .between(DriverWithdraw::getWithdrawTime, startDateTime, endDateTime)
                .eq(DriverWithdraw::getState, DriverWithdrawState.done)
                .select(DriverWithdraw::getAmount);
        List<DriverWithdraw> driverWithdraws = driverWithdrawMapper.selectList(queryWrapper);
        BigDecimal withDraw = driverWithdraws.stream().map(DriverWithdraw::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        resultMap.put("withDraw", ObjectUtil.isNull(withDraw) ? "0.0" : withDraw);

        //加价
        List<Map<String, Object>> penaltyMap = rideOrderMapper.selectMaps(new QueryWrapper<RideOrder>().select("IFNULL(SUM(price_markup),0) as amount")
                .lambda()
                .in(RideOrder::getState, Arrays.asList(RideOrderState.done))
                .between(RideOrder::getCreatedAt, startDateTime, endDateTime));
        resultMap.put("priceMarkup", penaltyMap.get(0).getOrDefault("amount", 0));


        //订单总收入
        Object totalAmountObj = rideOrderMapper.selectMaps(new QueryWrapper<RideOrder>().select("IFNULL(SUM(fare_paid),0) as amount")
                .lambda()
                .in(RideOrder::getState, Arrays.asList(RideOrderState.done, RideOrderState.operated))
                .eq(RideOrder::getRefunded, false)
                .between(RideOrder::getCreatedAt, startDateTime, endDateTime)).get(0).get("amount");

        //未付款金额
        Object notPayAmountObj = rideOrderMapper.selectMaps(new QueryWrapper<RideOrder>().select("IFNULL(SUM(fare_paid),0) as amount")
                .lambda()
                .in(RideOrder::getState, Arrays.asList(RideOrderState.operated))
                .eq(RideOrder::getRefunded, false)
                .between(RideOrder::getCreatedAt, startDateTime, endDateTime)).get(0).get("amount");

        //现金收入
        Object money = rideOrderMapper.selectMaps(new QueryWrapper<RideOrder>().select("IFNULL(SUM(fare_paid),0) as amount")
                .lambda()
                .in(RideOrder::getState, Arrays.asList(RideOrderState.done))
                .eq(RideOrder::getPaidThrough, RideOrderPaidThrough.cash)
                .eq(RideOrder::getRefunded, false)
                .between(RideOrder::getCreatedAt, startDateTime, endDateTime)).get(0).get("amount");

        //银行对账
        List<OrderBankBill> list = bankBillMapper.selectList(Wrappers.<OrderBankBill>lambdaQuery()
                .between(OrderBankBill::getDealCompleteDate, startDateTime, endDateTime));
        BigDecimal successAmount = list.stream().filter(x -> "ok".equals(x.getResult())).map(OrderBankBill::getDealAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        resultMap.put("successAmount", ObjectUtil.isNull(successAmount) ? 0 : successAmount);
        resultMap.put("totalAmount", ObjectUtil.isNull(totalAmountObj) ? 0 : totalAmountObj);
        resultMap.put("notPayAmountObj", notPayAmountObj);
        resultMap.put("money", money);
        return RestResult.success(resultMap);
    }

    // 收入统计
    @Override
    public FinanceIncomeTotal incomeTotal() {
        FinanceIncomeTotal ret = new FinanceIncomeTotal();
        // 银行余额以及 bankBalance
        String result = CmbPayClient.mbrBalQry();
        JSONObject jsonObject = JSON.parseObject(result);
        if (jsonObject.getInteger("code") == 10000) {
            JSONObject data = jsonObject.getJSONObject("data");
            BigDecimal avaBal = Convert.toBigDecimal(data.get("avaBal"), new BigDecimal(0));

            ret.setBankBalance(avaBal.toString());
        } else {
            throw new RuntimeException("余额查询异常");
        }


        // 公司收益 comIncome
        BigDecimal comIncome = dingdingSendMsgMapper.comIncome();
        //已提现信息服务费
        List<Map<String, Object>> amoutnList = companyWithdrawMapper.selectMaps(new QueryWrapper<CompanyWithdraw>()
                .select("IFNULL(SUM(amount),0) as amount")
                .lambda()
                .eq(CompanyWithdraw::getState, CompanyWithdrawState.done));
        ;
        ret.setComIncome(comIncome);
        BigDecimal companyWaitWithdraw = NumberUtil.sub(comIncome, Convert.toBigDecimal(amoutnList.get(0).getOrDefault("amount", 0)));
        ret.setCompanyWaitWithdraw(companyWaitWithdraw);
        return ret;
    }

    @SneakyThrows
    @Override
    public void incomeBelongStatisticsExport(AdminUser adminUser, FinanceIncomeBelongStatWrapper wrapper, HttpServletResponse response) {
        if (StrUtil.isBlank(wrapper.getSelectDate())) {
            wrapper.setSelectDate(DateUtil.formatDate(DateUtil.date()));
        }
        List<FinanceIncomeBelongStatsPresenter> result = new ArrayList<>();
        com.njtoyo.taxi.entity.backend.Enum.AccountType accountType = adminUser.getAccountType();
        List<Map<String, Object>> rows = new ArrayList<>();
        if (Objects.equals(com.njtoyo.taxi.entity.backend.Enum.AccountType.company, accountType)) {
            AdminUserIdentityPresenter adminUserIdentityPresenter = (AdminUserIdentityPresenter) adminUser.getIdentity();
            if (adminUserIdentityPresenter != null) {
                List<Long> companyIds = adminUserIdentityPresenter.getCompanyIds();
                LambdaQueryWrapper<Company> lambdaQueryWrapper = new LambdaQueryWrapper<>();
                lambdaQueryWrapper.in(Company::getId, companyIds);
                List<Company> companyList = companyMapper.selectList(lambdaQueryWrapper);
                doIncomeBelongStatistics(result, wrapper, companyList);
                Collections.sort(result, Comparator.comparing(m -> m.getOrderAmount(), Comparator.reverseOrder()));
                for (FinanceIncomeBelongStatsPresenter presenter : result) {
                    Map<String, Object> row = new HashMap<>();
                    row.put("地区", presenter.getName());
                    row.put("订单金额", presenter.getOrderAmount());
                    row.put("司机收入", presenter.getDriverAmount());
                    row.put("信息服务费", presenter.getInfoAmount());
                    rows.add(row);
                }
            }

        } else {
            doIncomeBelongStatistics(result, wrapper);
            Collections.sort(result, Comparator.comparing(m -> m.getOrderAmount(), Comparator.reverseOrder()));
            for (FinanceIncomeBelongStatsPresenter presenter : result) {
                Map<String, Object> row = new HashMap<>();
                row.put("地区", presenter.getName());
                row.put("订单金额", presenter.getOrderAmount());
                if (ObjectUtil.isNotNull(wrapper.getCantonId())) {
                    row.put("司机收入", presenter.getDriverAmount());
                    row.put("信息服务费", presenter.getInfoAmount());
                }

                rows.add(row);
            }
        }

        ExcelWriter writer = ExcelUtil.getWriter();
        StyleSet style = writer.getStyleSet();
        CellStyle cellStyle = style.getHeadCellStyle();
        cellStyle.setWrapText(true);
        writer.write(rows, true);
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=ride_order.xls");
        ServletOutputStream out = response.getOutputStream();

        writer.flush(out, true);
        writer.close();
        IoUtil.close(out);
    }

    @Override
    public FinanceIncomeTotal incomeCantonTotal(AdminUser adminUser) {
        FinanceIncomeTotal ret = new FinanceIncomeTotal();
        AdminUserIdentityPresenter identity = (AdminUserIdentityPresenter) adminUser.getIdentity();
        List<Long> companyIds = identity.getCompanyIds();
        if (adminUser.getAccountType() == com.njtoyo.taxi.entity.backend.Enum.AccountType.canton) {


        }
        return ret;
    }
}

